package com.wzc.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.wzc.basic.constant.RegisterConstant;
import com.wzc.basic.exception.BusinessException;
import com.wzc.basic.jwt.JwtUtils;
import com.wzc.basic.jwt.RsaUtils;
import com.wzc.basic.util.AjaxResult;
import com.wzc.basic.util.HttpUtil;
import com.wzc.basic.util.Md5Utils;
import com.wzc.basic.util.StrUtils;
import com.wzc.org.mapper.EmployeeMapper;
import com.wzc.system.domain.Menu;
import com.wzc.user.constant.WxConstants;
import com.wzc.user.domain.Logininfo;
import com.wzc.user.domain.User;
import com.wzc.user.domain.WxUser;
import com.wzc.user.dto.LoginDto;
import com.wzc.user.dto.WxDto;
import com.wzc.user.mapper.LogininfoMapper;
import com.wzc.user.mapper.UserMapper;
import com.wzc.user.mapper.WxUserMapper;
import com.wzc.user.service.ILoginService;
import com.wzc.user.util.LoginData;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 登录
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> account(LoginDto dto) {
        // 根据手机号码和type查询loginInfo
        Logininfo logininfo = logininfoMapper.getLogininfoByDto(dto);
        if(logininfo == null){ // 用户不存在
            throw new BusinessException("账号或密码错误!");
        }
        // 账户存在   判断密码是否正确   加盐加密
        String md5Password = Md5Utils.encrypByMd5(logininfo.getSalt() + dto.getPassword());
        // 与数据库中的密码做对比
        if(!md5Password.equals(logininfo.getPassword())){ // 密码错误
            throw new BusinessException("账号或密码错误!");
        }

//        // 账户和密码都正确，将数据保存到redis中，并设置过期时间
//        String token = UUID.randomUUID().toString(); // 随机生成一个key(token)
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//
//        HashMap<String, Object> map = new HashMap<>();
//        // 将数据存到map中
//        map.put("token",token);
//        logininfo.setSalt(null);
//        logininfo.setPassword(null);
//        map.put("logininfo",logininfo);

        // 调用 JWT 方法
        Map<String, Object> map = loginSuccessJwtHandler(logininfo);
        return map;
    }

    /**
     * JWT 封装参数 （当前用户信息，权限，菜单）
     * @param logininfo
     * @return
     */
    private Map<String, Object> loginSuccessJwtHandler(Logininfo logininfo) {

        try {
            LoginData loginData = new LoginData();
            loginData.setLogininfo(logininfo);
            Map<String, Object> map = new HashMap<>();
            // 判断登录人是否是管理人员，
            if( logininfo.getType() == 0){ // 管理人员
                // 返回当前登录人的权限
                List<String> permissions = employeeMapper.loadPermissionsByLogininfoId(logininfo.getId());

                // 当前登录人所有菜单
                List<Menu> menus = employeeMapper.loadMenusByLogininfoId(logininfo.getId());

                // 存入loginData
                loginData.setMenus(menus);
                loginData.setPermissions(permissions);

                // 存入map
                map.put("permissions",permissions);
                map.put("menus",menus);
            }

            //  加密
            //4.通过私钥对登录信息进行加密 - jwtToken串
            PrivateKey privateKey = RsaUtils.getPrivateKey(RsaUtils.class.getClassLoader()                                             	  .getResource("auth_rsa.pri").getFile());
            //将登陆人信息加密得到jwtToken串
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 3000);

            map.put("token",jwtToken);
            map.put("logininfo",logininfo);

            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

//    private Map<String, Object> loginSuccessJwtHandler(Logininfo logininfo) {
//        try {
//            LoginData loginData = new LoginData(); // 创建LoginData对象
//            loginData.setLogininfo(logininfo); // 将 logininfo 添加到 LoginData
//            Map<String, Object> map = new HashMap<>();
//            // 1.获取 logininfo
//            if(logininfo.getType() == 0){ // 判断是不是管理员
//                // 2.通过 logininfo  id 查询并获取当前登陆人的所有权限 permissions
//                List<String> permissions = employeeMapper.loadPermissionsByLogininfoId(logininfo.getId());
//                // 3.通过 logininfo  id 查询并 获取当前登陆人的所有菜单 menus
//                List<Menu> menus = employeeMapper.loadMenusByLogininfoId(logininfo.getId());
//                loginData.setMenus(menus); // 菜单  存到LoginData
//                loginData.setPermissions(permissions); // 权限  存到LoginData
//                // 将当前登陆人的权限和菜单添加到map - 响应给前端
//                map.put("permissions",permissions);
//                map.put("menus",menus);
//            }
//            // 4.通过私钥对登录信息进行加密 - jwtToken串
//            PrivateKey privateKey = RsaUtils.getPrivateKey(RsaUtils.class.getClassLoader()
//                    .getResource("auth_rsa.pri").getFile());
//            // 将登陆人信息加密得到jwtToken串
//            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 3000);
//
//            // 5.装到map返回
//            map.put("token",jwtToken);
//            map.put("logininfo",logininfo);
//            return map;
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return null;
//    }

    /**
     * 微信登录
     * @param code
     * @return
     */
    @Override
    public AjaxResult weChat(String code) {

        /**
         * 微信扫码----判断之前有没有绑定过
         *     1.先通过code获取到token和openid
         *      2. 根据openid查询wxuser
         *         3. openid存在，则说明之前绑定过，直接免密登录
         *           4. openid不存在，重新生成数据，进行绑定
         */

        // 1.先通过code获取到token和openid
        String jsonStr = HttpUtil.httpGet(WxConstants.GET_ACK_URL
                .replace("APPID", WxConstants.APPID)

                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code));

        JSONObject jsonObject = JSONObject.parseObject(jsonStr); // 将jsonStr(json字符串)转为 json对象
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");

        System.out.println(access_token);

        // 2. 根据openid查询wxuser
        WxUser dbWxUser = wxUserMapper.loadByOpenid(openid);
        //   WxUser有信息，        有user_id,已经绑定了
        if (dbWxUser != null && dbWxUser.getUser_id() != null){
            // 之前绑定了，直接免密登录

            // 获取logininfo信息。
            Logininfo logininfo = logininfoMapper.loadByUserId(dbWxUser.getUser_id());
//            String token = UUID.randomUUID().toString(); // 随机生成一个key(token)
//            redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES); // 设置过期时间
//
//            HashMap<String, Object> map = new HashMap<>();
//            // 将数据存到map中
//            map.put("token",token);
//            logininfo.setSalt(null);
//            logininfo.setPassword(null);
//            map.put("logininfo",logininfo);

            // 调用 JWT 方法
            Map<String, Object> map = loginSuccessJwtHandler(logininfo);

            return AjaxResult.me().setResultObj(map);

        } else { // 第一次通过扫码登录
            // 携带参数 access_token 和 openid 返回给前端 发送第三次请求
            String param = "?access_token="+access_token+"&openid="+openid;
            return AjaxResult.me().setMsg("绑定").setResultObj(param);
        }

    }


    /**
     * 微信扫码登录
     * @param dto
     * @return
     */
    @Override
    public AjaxResult binder(WxDto dto) {
        // 获取参数
        String phone = dto.getPhone();
        String verifyCode = dto.getVerifyCode();
        String access_token = dto.getAccess_token();
        String openid = dto.getOpenid();

        // 1. 非空校验
        if(StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(verifyCode)
                || StringUtils.isEmpty(access_token)
                || StringUtils.isEmpty(openid)){
            throw new BusinessException("参数不能为空！");
        }

        // 验证码是否正确或过期
        Object obj = redisTemplate.opsForValue().get(phone + RegisterConstant.WXBINDER);
        if(obj == null){
            throw new BusinessException("验证码已过期，请重新获取！");
        }
        // 获取系统生成的验证码与用户输入的（verifyCode）对比
        if(!verifyCode.equalsIgnoreCase(obj.toString().split(":")[0])){
            throw new BusinessException("验证码输入错误，请重新输入！");
        }

        // 向微信开发平台发送第三次请求，获取wxuser信息(返回值)
        // 将请求路径中的 ACCESS_TOKEN 替换 access_token        OPENID 替换 openid
        String jsonObj = HttpUtil.httpGet(WxConstants.GET_WXUSER_URL
                .replace("ACCESS_TOKEN", access_token)
                .replace("OPENID", openid));

        // 获取wxuser
        WxUser wxUser = jsonObj2WxUser(jsonObj);

        // 通过手机号码查询user信息，能够查到就直接绑定，查不到，自己生成一个user logininfo再进行绑定
        User dbUser = userMapper.findByPhone(phone);
        if(dbUser == null){ // 没有user信息
            dbUser = phone2User(phone); // 生成一个user对象
            Logininfo dblogininfo = user2Logininfo(dbUser); // 再通过生成的user对象生成一个logininfo对象
            logininfoMapper.save(dblogininfo); // 将dblogininfo保存到数据库中，返回自增的主键
            dbUser.setLogininfo_id(dblogininfo.getId()); // 将dblogininfo自增的主键赋值给user关联外键Logininfo_id
            userMapper.save(dbUser); // 将user保存到数据库
        }
        // 绑定wxuser
        wxUser.setUser_id(dbUser.getId()); // 将dbUser自增的主键赋值给wxuser关联外键user_id
        wxUserMapper.save(wxUser); // 将绑定后的wxuser保存到数据库

        // 绑定完了，就可以免密登录了

        // 通过user中的logininfo_id查询logininfo
        Logininfo logininfo = logininfoMapper.loadById(dbUser.getLogininfo_id());
        logininfo.setType(logininfo.getType());
//        String token = UUID.randomUUID().toString(); // 随机生成一个key(token)
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES); // 设置过期时间
//
//        HashMap<String, Object> map = new HashMap<>();
//        // 将数据存到map中
//        map.put("token",token);
//        logininfo.setSalt(null);
//        logininfo.setPassword(null);
//        map.put("logininfo",logininfo);

        // 调用 JWT 方法
        Map<String, Object> map = loginSuccessJwtHandler(logininfo);
        return AjaxResult.me().setResultObj(map);
    }

    /**
     * 生成一个logininfo对象
     * @param dbUser
     * @return
     */
    private Logininfo user2Logininfo(User dbUser) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(dbUser,logininfo);
        logininfo.setType(1);
        return logininfo;
    }

    /**
     * 生成一个user对象
     * @param phone
     * @return
     */
    private User phone2User(String phone) {
        User user = new User();
        user.setUsername(phone); // 用户名
        user.setPhone(phone); // 电话

        String salt = StrUtils.getComplexRandomString(32); // 随机生成一个盐值
        user.setSalt(salt);

        String password = StrUtils.getComplexRandomString(6); // 随机生成一个密码
        String dbpassword = Md5Utils.encrypByMd5(salt + password); // 加盐加密
        user.setPassword(dbpassword);

        return user;
    }

    /**
     * 生成一个wxuser对象
     * @param jsonObj
     * @return
     */
    private WxUser jsonObj2WxUser(String jsonObj) {
        // 将参数jsonObj（字符串）转为json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonObj);

        WxUser wxUser = new WxUser();
        wxUser.setAddress(jsonObject.getString("country"));
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        return wxUser;
    }


}
